package com.kolalo.syntaxtree.trees;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map.Entry;
import com.kolalo.constant.keywords.*;

public class FunctionTree extends BaseTree {
    public String FunctionName;
    public String FunctionVisibility;
    public boolean IsFinal;
    public boolean IsStatic;
    public boolean IsSynchronized;
    public boolean IsNative;
    public boolean IsStrictfp;
    public List<String> FunctionThrows = new ArrayList<String>();
    public HashMap<String, String> FunctionParams = new HashMap<String, String>();
    public String FunctionBody;
    public String FunctionHeader;

    public FunctionTree(String funcCode) {
        super(funcCode);
        initLocalVariable();
    }

    private void initLocalVariable() {
        this.FunctionHeader = getFunctionHeader(this.Code);
        this.FunctionBody = getFunctionBody(this.Code);
        this.FunctionName = getFunctionName(this.Code);
        this.FunctionParams = getFunctionParams(this.Code);
        this.FunctionThrows = getFunctionThrows(this.Code);
        String[] split = headerSplit(this.Code);
        this.FunctionVisibility = Visibility.PRIVATE;
        this.IsStatic = false;
        this.IsFinal = false;
        this.IsNative = false;
        this.IsSynchronized = false;
        this.IsStrictfp = false;
        for (int i = 0; i < split.length; i++) {
            if (split[i].equals(Visibility.PUBLIC))
                this.FunctionVisibility = Visibility.PUBLIC;
            if (split[i].equals(Visibility.PRIVATE))
                this.FunctionVisibility = Visibility.PRIVATE;
            if (split[i].equals(Visibility.PROTECTED))
                this.FunctionVisibility = Visibility.PROTECTED;
            if (split[i].equals(Visibility.DEFAULT))
                this.FunctionVisibility = Visibility.DEFAULT;
            if (split[i].equals(ModifierCharacter.STATIC))
                this.IsStatic = true;
            if (split[i].equals(ModifierCharacter.FINAL))
                this.IsFinal = true;
            if (split[i].equals(ModifierCharacter.NATIVE))
                this.IsNative = true;
            if (split[i].equals(ModifierCharacter.SYNCHRONIZED))
                this.IsSynchronized = true;
            if (split[i].equals(ModifierCharacter.STRICTFP))
                this.IsStrictfp = true;
        }
    }

    private String getFunctionHeader(String funcCode) {
        return funcCode.substring(0, funcCode.indexOf(')') + 1);
    }

    private String getFunctionBody(String funcCode) {
        return funcCode.substring(funcCode.indexOf('{'));
    }

    private List<String> getFunctionThrows(String funcCode) {
        String throwsStr = funcCode.substring(funcCode.indexOf(')') + 1, funcCode.indexOf('{'))
                .replace(ModifierCharacter.THROWS, Other.Empty).replaceAll("\\s+", Other.Empty);
        List<String> throwsList = new ArrayList<String>();
        if (!throwsStr.isEmpty()) {
            String[] split = throwsStr.split(",");
            for (int i = 0; i < split.length; i++) {
                throwsList.add(split[i]);
            }
        }
        return throwsList;
    }

    private HashMap<String, String> getFunctionParams(String funcCode) {
        String functionHeader = getFunctionHeader(funcCode);
        String subParamsStr = functionHeader.substring(functionHeader.indexOf('(') + 1, functionHeader.indexOf(')'));
        HashMap<String, String> hashMap = new HashMap<String, String>();
        if (subParamsStr.isEmpty())
            return hashMap;
        String[] params = subParamsStr.split(",");
        for (int i = 0; i < params.length; i++) {
            if (params[i].contains("...")) {
                String[] tempSplit = params[i].split("\\.\\.\\.");
                hashMap.put(tempSplit[1].trim(), tempSplit[0].trim().concat("..."));
            } else if (params[i].contains("[")) {
                String[] tempSplit = params[i].split("\\[");
                hashMap.put(tempSplit[1].replace("]", Other.Empty).trim(), tempSplit[0].trim().concat("[]"));
            } else if (params[i].replaceAll("\\s+", " ").length() >= 3) {
                String[] tempSplit = params[i].split(" ");
                hashMap.put(tempSplit[1].trim(), tempSplit[0].trim());
            }
        }
        return hashMap;
    }

    private String getFunctionName(String funcCode) {
        String[] split = headerSplit(funcCode);
        return split[split.length - 1];
    }

    private String[] headerSplit(String funcCode) {
        String functionHeader = getFunctionHeader(funcCode);
        String functionHeaderNotParam = functionHeader.substring(0, functionHeader.indexOf('('));
        String[] split = functionHeaderNotParam.split(" ");
        if (split.length < 2)
            throw new IllegalArgumentException(String.format("函数头(%s)异常", functionHeaderNotParam));
        return split;
    }

    public boolean equal(FunctionTree f) {
        if (this.equals(f)) return true;
        if (this.FunctionName.equals(f.FunctionName) && this.FunctionParams.size() == f.FunctionParams.size()&&this.FunctionParams.equals(f.FunctionParams)) {
            for (String set : f.FunctionParams.keySet()) {
                if (!this.FunctionParams.containsKey(set)) return false;
            }
            return true;
        }
        return false;
    }

    public void showInfo() {
        System.out.println(String.format("源代码:%s", this.Code));
        System.out.println(String.format("函数名:%s", this.FunctionName));
        System.out.println(String.format("函数可见性:%s", this.FunctionVisibility));
        System.out.println(String.format("函数头:%s", this.FunctionHeader));
        System.out.println(String.format("函数体:%s", this.FunctionBody));
        ArrayList<String> list = new ArrayList<String>();
        for (Entry<String, String> entry : this.FunctionParams.entrySet()) {
            list.add(String.format("[%s,%s]", entry.getKey(), entry.getValue()));
        }
        System.out.println(String.format("参数表:%s", String.join(",", list)));
        System.out.println(String.format("异常表:%s", String.join(",", this.FunctionThrows)));
        System.out.println(String.format("is static:%s", this.IsStatic));
        System.out.println(String.format("is final:%s", this.IsFinal));
        System.out.println(String.format("is native:%s", this.IsNative));
        System.out.println(String.format("is strictfp:%s", this.IsStrictfp));
        System.out.println(String.format("is synchronized:%s", this.IsSynchronized));
    }
}
